//---------------------------------------------------------------------
// File: WaitOnFileExStep.cs
// 
// Summary: 
//
// Copyright (c) Hammersmith & Fulham Bridge Partnership. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, WHETHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//---------------------------------------------------------------------

using System;
using System.IO;
using System.Threading;
using System.Xml;
using Microsoft.Services.BizTalkApplicationFramework.BizUnit;

namespace BizUnit.Extensions
{
	/// <summary>
	/// The WaitOnFileExStep is used to wait for a FILE to be written to a given location.
	/// It uses the file system watcher. It replaces the old step which used to simply delay
	/// for the given interval.Now when the file is detected the system returns immediately.
	/// It also loads the file name(both the full name and the simple name) into the context 
	/// (with the keynames WaitedFileFullName and WaitedFileName) and allows you to specify whether you want 
	/// to consider previously existing files which is useful in scenarios where the testing is
	/// intensive and the system races ahead of the test and places the file in the location and
	/// the step executes after the file is already available.
	/// </summary>
	/// 
	/// <remarks>
	/// The following shows an example of the Xml representation of this test step.
	/// 
	/// <code escaped="true">
	///	<TestStep assemblyPath="BizUnit.Extensions.dll" typeName="BizUnit.Extensions.WaitOnFileExStep">
	///		<Path>n:\\</Path>
	///		<FileFilter>*.xml</FileFilter>
	///		<TimeOut>10000</TimeOut>
	///		<IncludeOldFiles>Y</IncludeOldFiles>
	///		<Delete>Y</Delete>
	///	</TestStep>
	///	</code>
	///	
	///	<list type="table">
	///		<listheader>
	///			<term>Tag</term>
	///			<description>Description</description>
	///		</listheader>
	///		<item>
	///			<term>Path</term>
	///			<description>The directory to look for the FILE</description>
	///		</item>
	///		<item>
	///			<term>FileFilter</term>
	///			<description>The FILE mask to be used to search for a FILE, e.g. *.xml</description>
	///		</item>
	///		<item>
	///			<term>TimeOut</term>
	///			<description>The time to wait for the FILE to become present in miliseconds</description>
	///		</item>
	///		<item>
	///			<term>Delete</term>
	///			<description>Do you want to delete the file when checked (Y/N)</description>
	///		</item>
	///		<item>
	///		<term>IncludeOldFiles</term>
	///		<description>should we take into account files already existing</description>
	///		</item>
	///	</list>
	///	</remarks>

	public class WaitOnFileExStep: ITestStep
	{
		ManualResetEvent mre;
		string newFilePath;
		string newFileName;

		/// <summary>
		/// ITestStep.Execute() implementation
		/// </summary>
		/// <param name='testConfig'>The Xml fragment containing the configuration for this test step</param>
		/// <param name='context'>The context for the test, this holds state that is passed beteen tests</param>
		public void Execute(XmlNode testConfig, Context context)
		{
			// read test config...
			string path = context.ReadConfigAsString( testConfig, "Path" );
			string fileFilter = context.ReadConfigAsString( testConfig, "FileFilter" );
			int timeOut = context.ReadConfigAsInt32( testConfig, "TimeOut" );
			string deleteFileIfFound = context.ReadConfigAsString(testConfig,"Delete",true);
			string includeOldFiles = context.ReadConfigAsString(testConfig,"IncludeOldFiles");
			if ( (Directory.GetFiles(path).Length > 0 ) && (includeOldFiles == "Y" ))
			{
				// Set newFilename and newFilePath with the earliest file
				string [] items = Directory.GetFileSystemEntries(path,"*.xml");
				foreach (string item in items )
				{
					context.Add("WaitedFileFullName",path + @"\" + item,true);
					context.Add("WaitedFileName",item,true);
					break;
				}

				return ;
			}
			FileSystemWatcher watcher = new FileSystemWatcher(); 
			watcher.Path = path; 
			watcher.Filter = fileFilter; 
			watcher.NotifyFilter = NotifyFilters.LastWrite; 
			watcher.EnableRaisingEvents = true; 
			watcher.IncludeSubdirectories = false;
			watcher.Changed += new FileSystemEventHandler(OnCreated);
			mre = new ManualResetEvent(false);

			if(!mre.WaitOne(timeOut, false))
			{
				throw new Exception(string.Format("WaitOnFileStep timed out after {0} milisecs watching path:{1}, filter{2}", timeOut, path, fileFilter));
			}
			else
			{
				context.LogInfo(string.Format("WaitOnFileStep found the file: {0}", newFilePath));
				context.Add("WaitedFile",newFilePath,true);
				context.Add("WaitedFileName",newFileName,true);
								
			}
			if (deleteFileIfFound == "Y")
			{
				Thread.Sleep(500); // Wait for file to be closed by creator
				File.Delete(newFilePath);
				context.LogWarning("FILE {0} HAS BEEN DELETED",newFilePath);
			}
            XmlNode validationConfig = testConfig.SelectSingleNode("ValidationStep");
            context.ExecuteValidator(null, validationConfig);
		}

		private void OnCreated(object sender, FileSystemEventArgs e) 
		{ 
			newFilePath = e.FullPath;
			newFileName = e.Name;
			mre.Set();
		}
	}
}
